package gl.drawer

import gl.util.Resource
import gl.window
import org.joml.Matrix4f
import org.lwjgl.glfw.GLFW
import org.lwjgl.opengl.GL44C.*

/**
 * 图元重启动
 */
class Restart: Drawer() {

    private val positions = floatArrayOf(
        -1f, -1f, -1f, 1f,
        -1f, -1f,  1f, 1f,
        -1f,  1f, -1f, 1f,
        -1f,  1f,  1f, 1f,
         1f, -1f, -1f, 1f,
         1f, -1f,  1f, 1f,
         1f,  1f, -1f, 1f,
         1f,  1f,  1f, 1f,
    )

    private val colors = floatArrayOf(
        1f, 1f, 1f, 1f,
        1f, 1f, 0f, 1f,
        1f, 0f, 1f, 1f,
        1f, 0f, 0f, 1f,
        0f, 1f, 1f, 1f,
        0f, 1f, 0f, 1f,
        0f, 0f, 1f, 1f,
       .5f,.5f,.5f, 1f,
    )

    // 一个完整的立方体，但实际上是由两个条带线组成的
    private val indices = intArrayOf(
        0, 1, 2, 3, 6, 7, 4, 5, // line0
        0xFFFF, // restart index
        2, 6, 0, 4, 1, 5, 3, 7, // line1
    )

    private var aspect = 1f

    private var program = 0
    private var bufferIndices = 0
    private var bufferVertices = 0
    private var vertex = 0
    private var cullFace = false
    private var depthTest = false

    private var uniModelMatrix = 0
    private var uniProjectionMatrix = 0

    override fun initialize() {
        // shader
        val vs = glCreateShader(GL_VERTEX_SHADER)
        glShaderSource(GL_VERTEX_SHADER, Resource.getString("primitive_restart.vs.glsl"))
        glCompileShader(vs)
        val fs = glCreateShader(GL_FRAGMENT_SHADER)
        glShaderSource(GL_FRAGMENT_SHADER, Resource.getString("primitive_restart.fs.glsl"))
        glCompileShader(fs)
        program = glCreateProgram()
        glAttachShader(program, vs)
        glAttachShader(program, fs)
        glLinkProgram(program)
        glDetachShader(program, vs)
        glDetachShader(program, fs)
        glDeleteShader(vs)
        glDeleteShader(fs)
        glLinkProgram(program)
        glUseProgram(program)

        // buffer and vertex
        bufferIndices = glGenBuffers()
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIndices)
        glBufferStorage(GL_ELEMENT_ARRAY_BUFFER, indices.size * 4L, 0)
        glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0L, indices)
        bufferVertices = glGenBuffers()
        glBindBuffer(GL_ARRAY_BUFFER, bufferVertices)
        glBufferStorage(GL_ARRAY_BUFFER, (positions.size + colors.size) * 4L, 0)
        glBufferSubData(GL_ARRAY_BUFFER, 0L, positions)
        glBufferSubData(GL_ARRAY_BUFFER, positions.size * 4L, colors)

        vertex = glGenVertexArrays()
        glBindVertexArray(vertex)
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(0, 4, GL_FLOAT, false, 0, 0L)
        glEnableVertexAttribArray(1)
        glVertexAttribPointer(0, 4, GL_FLOAT, false, 0, positions.size * 4L)

        // 其他
        cullFace = glIsEnabled(GL_CULL_FACE)
        depthTest = glIsEnabled(GL_DEPTH_TEST)
    }

    override fun resume() {
        super.resume()
        glClearColor(0f, 0f, 0f, 1f)
        onResize(window.width, window.height)

        glUseProgram(program)
        uniModelMatrix = glGetUniformLocation(program, "model_matrix")
        uniProjectionMatrix = glGetUniformLocation(program, "projection_matrix")

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIndices)
        glBindBuffer(GL_ARRAY_BUFFER, bufferVertices)
        glBindVertexArray(vertex)
        glEnableVertexAttribArray(0)
        glEnableVertexAttribArray(1)

        glEnable(GL_CULL_FACE)
        glDisable(GL_DEPTH_TEST)
        // !! use restart
        glEnable(GL_PRIMITIVE_RESTART)
        glPrimitiveRestartIndex(0xFFFF)
    }

    override fun update(): Boolean {
        super.update()
        val time = GLFW.glfwGetTime()
        glUniformMatrix4fv(uniModelMatrix, false, Matrix4f().identity().translate(0f, 0f, -5f)
            .rotateY((time).toFloat())
            .rotateZ((time * 2).toFloat())[FloatArray(16)])
        glDrawElements(GL_TRIANGLE_STRIP, 17, GL_UNSIGNED_INT, 0L)
        return false
    }

    override fun stop() {
        super.stop()
        glDisableVertexAttribArray(0)
        glDisableVertexAttribArray(1)
        glBindVertexArray(0)
        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
        glUseProgram(0)
        if (!cullFace) {
            glDisable(GL_CULL_FACE)
        }
        if (depthTest) {
            glEnable(GL_DEPTH_TEST)
        }
        glDisable(GL_PRIMITIVE_RESTART)
    }

    override fun terminal() {
        glDeleteProgram(program)
        glDeleteVertexArrays(vertex)
        glDeleteBuffers(intArrayOf(bufferIndices, bufferVertices))
    }

    override fun onResize(width: Int, height: Int) {
        aspect = height / width.toFloat()
        if (isInitialized) {
            glUniformMatrix4fv(uniProjectionMatrix, false,
                Matrix4f().frustum(-1f, 1f, -aspect, aspect, 1f, 500f)[FloatArray(16)])
        }
    }
}